﻿using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.UI;
using GMBase.UI;

    /// <summary>
    /// ui界面的sort layer
    /// </summary>
    class UISortLayer
    {
        public const string UI_UnderScene = "UI_UnderScene";
        public const string UI_Scene = "UI_Scene";
        public const string UI_BottomMost = "UI_BottomMost";
        public const string UI_Bottom = "UI_Bottom";
        public const string UI_Lower = "UI_Lower";
        public const string UI_Normal = "UI_Normal";
        public const string UI_Upper = "UI_Upper";
        public const string UI_Top = "UI_Top";
        public const string UI_TopMost = "UI_TopMost";
        public const string UI_Screen = "UI_Screen";
        public const string UI_Debug = "UI_Debug";

        public static string GetUISortLayer(int windowLayer)
        {
            WindowLayer layer = (WindowLayer)windowLayer;
            switch (layer)
            {
                case WindowLayer.UnderScene:
                    return UI_UnderScene;
                case WindowLayer.Scene:
                    return UI_Scene;
                case WindowLayer.BottomMost:
                    return UI_BottomMost;
                case WindowLayer.Bottom:
                    return UI_Bottom;
                case WindowLayer.Lower:
                    return UI_Lower;
                case WindowLayer.Normal:
                    return UI_Normal;
                case WindowLayer.Upper:
                    return UI_Upper;
                case WindowLayer.Top:
                    return UI_Top;
                case WindowLayer.TopMost:
                    return UI_TopMost;
                case WindowLayer.Screen:
                    return UI_Screen;
                case WindowLayer.Debug:
                    return UI_Debug;
            }
            return UI_Normal;
        }
    }

    /// <summary>
    /// cull的应用方式
    /// </summary>
    public enum CullApplyMode
    {
        /// <summary>
        /// 不应用cull
        /// </summary>
        None,
        /// <summary>
        /// 当前父节点在cull中时应用cull
        /// </summary>
        WhenCulling,
        /// <summary>
        /// 任何情况下都应用cull
        /// </summary>
        Always
    }

    /// <summary>
    /// ui的场景层上的子层，最大为GWindow.SceneSubLayerCount -1
    /// </summary>
    public class SceneUILayer
    {
        /// <summary>
        /// npc信息（名称）层
        /// </summary>
        public const int NpcInfo = 0;
        /// <summary>
        /// 伤害数字层
        /// </summary>
        public const int HurtDig = 1;

        public const int Count = 5;
    }

    /// <summary>
    /// 一个窗口（或者层）所属的canvas
    /// </summary>
    class WinCanvas : IWinCanvas
    {
        private static Stack<WinCanvas> s_Pool = new Stack<WinCanvas>();
        public static WinCanvas Create(GameObject layerRoot, int winLayer, int orderInLayer, bool caster, bool cull)
        {
            WinCanvas wc;
            if (s_Pool.Count > 0)
            {
                wc = s_Pool.Pop();
            }
            else
            {
                wc = new WinCanvas(new GameObject("wc"), false);
            }
            wc.AttachToLayer(layerRoot, winLayer, orderInLayer, caster, cull);
            return wc;
        }
        public static void Gabage(WinCanvas wc, bool currentCulling)
        {
            if (s_Pool.Count < 10)
            {
                if (currentCulling)
                    wc.root.SetCull(false, true);
                wc.root.transform.SetParent(WindowMgr.me.poolRoot.transform);
                s_Pool.Push(wc);
            }
            else
            {
                U3DUtil.DestroyGameObject(wc.root);
            }
        }

        private GameObject _Root;
        private Canvas _Canvas;
        private GraphicRaycaster _Raycaster;
        private int _Layer;
        private bool _Shared;
        /// <summary>
        /// 构建一个win canvas
        /// </summary>
        /// <param name="aRoot">canvas所附着的对象</param>
        /// <param name="aShared">是否多窗口共享canvas</param>
        public WinCanvas(GameObject aRoot, bool aShared)
        {
            aRoot.layer = LayerIndex.UI;
            RectTransform rect = aRoot.GetComponent<RectTransform>();
            if (rect == null)
            {
                rect = aRoot.AddComponent<RectTransform>();
            }
            rect.anchoredPosition3D = new Vector3(0.0f, 0.0f, 100.0f);
            rect.sizeDelta = new Vector2(1136.0f, 640.0f);
            rect.anchorMin = new Vector2(0.0f, 0.0f);
            rect.anchorMax = new Vector2(0.0f, 0.0f);
            rect.pivot = new Vector2(0.5f, 0.5f);
            Canvas canvas = aRoot.GetComponent<Canvas>();
            if (canvas == null)
            {
                canvas = aRoot.AddComponent<Canvas>();
            }
            canvas.renderMode = RenderMode.ScreenSpaceCamera;
            canvas.pixelPerfect = false;
            canvas.worldCamera = WindowMgr.me.uiCamera;
            canvas.planeDistance = 100.0f;
            canvas.referencePixelsPerUnit = 1.0f;
            CanvasScaler canvasScaler = aRoot.GetComponent<CanvasScaler>();
            if (canvasScaler == null)
            {
                canvasScaler = aRoot.AddComponent<CanvasScaler>();
                canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
                canvasScaler.referenceResolution = new Vector2(1136.0f, 640.0f);
                canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
                canvasScaler.matchWidthOrHeight = 0.0f;
                canvasScaler.referencePixelsPerUnit = 1.0f;
            }
            GraphicRaycaster raycaster = aRoot.GetComponent<GraphicRaycaster>();
            if (raycaster == null)
            {
                raycaster = aRoot.AddComponent<GraphicRaycaster>();
            }
            raycaster.ignoreReversedGraphics = true;
            raycaster.blockingObjects = GraphicRaycaster.BlockingObjects.None;

            _Root = aRoot;
            _Canvas = canvas;
            _Raycaster = raycaster;
            _Layer = -3;
            _Shared = aShared;
        }
        /// <summary>
        /// canvas所在的根节点
        /// </summary>
        public GameObject root
        {
            get { return _Root; }
        }
        /// <summary>
        /// 是否多个窗口共享canvas
        /// </summary>
        public bool shared
        {
            get { return _Shared; }
        }
        /// <summary>
        /// canvas所在的层级
        /// </summary>
        public int layer
        {
            get { return _Layer; }
        }
        /// <summary>
        /// canvas在所在层级内的排序索引
        /// </summary>
        public int order
        {
            get { return _Canvas.sortingOrder; }
            set
            {
                if (_Canvas.sortingOrder != value)
                {
                    _Canvas.sortingOrder = value;
                }
            }
        }
        /// <summary>
        /// 检测器
        /// </summary>
        public GraphicRaycaster rayCaster
        {
            get { return _Raycaster; }
        }
        /// <summary>
        /// 将canvas加入到给定层级里
        /// </summary>
        /// <param name="layerRoot">层级的根节点，如果这个根节点与当前canvas的根节点一致，说明层级直接持有唯一的一个canva</param>
        /// <param name="winLayer">层级</param>
        /// <param name="orderInLayer">在层级中的排序索引</param>
        /// <param name="caster">是否启用点击</param>
        /// <param name="cull">是否cull掉</param>
        public void AttachToLayer(GameObject layerRoot, int winLayer, int orderInLayer, bool caster, bool cull)
        {
            if (layerRoot != _Root && _Root.transform.parent != layerRoot.transform)
            {//重新挂接
                _Root.transform.SetParent(layerRoot.transform);
                _Root.SetCull(cull, false);
            }
            if (_Layer != winLayer)
            {
                _Layer = winLayer;
                _Canvas.sortingLayerName = UISortLayer.GetUISortLayer(winLayer);
                _Canvas.sortingOrder = orderInLayer;
            }
            else if (_Canvas.sortingOrder != orderInLayer)
            {
                _Canvas.sortingOrder = orderInLayer;
            }
            _Raycaster.enabled = caster;
        }
    }

    /// <summary>
    /// 一个窗口层的管理器
    /// </summary>
    public class WindowLayerHolder
    {
        private const int BaseOrder = 0;
        private const int OrderStep = 10;

        private int _Layer;
        private bool _Caster;
        private bool _Cull;
        private GameObject _Root;
        private WinCanvas _SharedCanvas;
        private List<WinCanvas> _WinCanvasList;
        private List<GameObject> _SubLayers;
        private int _MaxOrder;
        public WindowLayerHolder(GameObject aLayerRoot, int winLayer)
        {
            _Layer = winLayer;
            _Caster = IsLayerCastered(_Layer);
            _Cull = false;
            _MaxOrder = 0;
            _Root = aLayerRoot;
            _Root.layer = LayerIndex.UI;
            _SharedCanvas = null;
            _SubLayers = null;
            if (winLayer == (int)WindowLayer.Bottom
                || winLayer == (int)WindowLayer.Lower)
            {//这两个层级的窗口，每个窗口都包含一个canvas
                _WinCanvasList = new List<WinCanvas>();
                _MaxOrder = BaseOrder - OrderStep;
            }
            else
            {//其它层级，一个层级包含一个唯一的canvas
                _MaxOrder = BaseOrder;
                _SharedCanvas = new WinCanvas(_Root, true);
                _SharedCanvas.AttachToLayer(_Root, _Layer, _MaxOrder, _Caster, _Cull);
                //if (_Layer == (int)WindowLayer.Scene && Game.running)
                if (_Layer == (int)WindowLayer.Scene)
                {//场景层有子层级
                    _SubLayers = new List<GameObject>();
                    for (int i = 0; i < SceneUILayer.Count; ++i)
                    {
                        GameObject subLayer = new GameObject("SubLayer" + i);
                        subLayer.layer = LayerIndex.UI;
                        subLayer.transform.SetParent(_Root.transform);
                        subLayer.transform.localPosition = Vector3.zero;
                        subLayer.transform.localScale = Vector3.one;
                        RectTransform tr = subLayer.AddComponent<RectTransform>();
                        tr.anchorMin = Vector2.zero;
                        tr.anchorMax = Vector2.one;
                        tr.offsetMin = Vector2.zero;
                        tr.offsetMax = Vector2.zero;
                        _SubLayers.Add(subLayer);
                    }
                }
            }
        }
        /// <summary>
        /// 当前层的根节点
        /// </summary>
        public GameObject root
        {
            get { return _Root; }
        }
        /// <summary>
        /// 当前层级是否cull
        /// </summary>
        public bool cull
        {
            get { return _Cull; }
            set
            {
                if (_Cull == value)
                    return;
                _Cull = value;
                _Root.SetCull(value, true);
            }
        }
        /// <summary>
        /// 当前层的名称
        /// </summary>
        public string layerName
        {
            get
            {
                WindowLayer lyer = (WindowLayer)_Layer;
                return System.Enum.GetName(typeof(WindowLayer), lyer);
            }
        }
        /// <summary>
        /// 直接添加一个对象到根节点上
        /// </summary>
        /// <param name="obj">待添加的对象</param>
        /// <param name="cullApplyMode">cull应用方式</param>
        public void AddGameObject(GameObject obj, CullApplyMode cullApplyMode)
        {
            obj.transform.SetParent(_Root.transform, false);
            //GuiUtil.ApplyCull(obj, CullApplyMode.WhenCulling, _Cull, true);
        }
        /// <summary>
        /// 直接添加一个对象到子层级上
        /// </summary>
        /// <param name="obj">待添加的对象</param>
        /// <param name="subLayerIndex">子层级索引，超过范围抛出异常</param>
        /// <param name="cullApplyMode">cull应用方式</param>
        public void AddGameObject(GameObject obj, int subLayerIndex, CullApplyMode cullApplyMode)
        {
            if (_SubLayers == null)
            {
                throw new Exception(string.Format("不能将对象添加到{0}层的子层", layerName));
            }
            if (subLayerIndex < 0 || subLayerIndex >= _SubLayers.Count)
            {
                throw new Exception(string.Format("索引{0}超出{1}层的子层索引", subLayerIndex, layerName));
            }
            obj.transform.SetParent(_SubLayers[subLayerIndex].transform, false);
            //GuiUtil.ApplyCull(obj, cullApplyMode, _Cull, true);
        }
        /// <summary>
        /// 向当前层添加一个窗口
        /// </summary>
        /// <param name="win">待添加的窗口</param>
        /// <param name="cullApplyMode">cull应用方式</param>
        public void AddWindow(GWindow win, CullApplyMode cullApplyMode)
        {
            WinCanvas wc = _SharedCanvas;
            if (wc == null)
            {
                _MaxOrder += OrderStep;
                wc = WinCanvas.Create(_Root, _Layer, _MaxOrder, _Caster, _Cull);
                win.winCanvas = wc;
                _WinCanvasList.Add(wc);
#if UNITY_EDITOR
                wc.root.name = string.Format("wc({0})", win.Root.name);
#endif
            }
            win.transform.SetParent(wc.root.transform, false);
            win.Layer = (WindowLayer)_Layer;
            //GuiUtil.ApplyCull(win.Root, cullApplyMode, _Cull, true);
        }

        /// <summary>
        /// 向当前层的某个子层添加一个窗口
        /// 如果索引超出，抛出异常
        /// </summary>
        /// <param name="win">待添加的窗口</param>
        /// <param name="subLayerIndex">子层索引</param>
        /// <param name="cullApplyMode">cull应用方式</param>
        public void AddWindow(GWindow win, int subLayerIndex, CullApplyMode cullApplyMode)
        {
            if (_SubLayers == null)
            {
                throw new Exception(string.Format("不能将窗口添加到{0}层的子层", layerName));
            }
            if (subLayerIndex < 0 || subLayerIndex >= _SubLayers.Count)
            {
                throw new Exception(string.Format("索引{0}超出{1}层的子层索引", subLayerIndex, layerName));
            }
            win.transform.SetParent(_SubLayers[subLayerIndex].transform, false);
            win.Layer = (WindowLayer)_Layer;
            //GuiUtil.ApplyCull(win.Root, cullApplyMode, _Cull, true);
        }
        /// <summary>
        /// 将给定窗口添加到目标窗口里（作为子窗口）
        /// </summary>
        /// <param name="win">给定窗口</param>
        /// <param name="parentWin">父窗口</param>
        /// <param name="parentRoot">窗口挂载的目标父节点，传null表示使用窗口根节点</param>
        /// <param name="cullApplyMode">cull应用方式</param>
        public void AddWindow(GWindow win, GWindow parentWin, GameObject parentRoot, CullApplyMode cullApplyMode)
        {
            if (parentRoot == null)
                parentRoot = parentWin.Root;
            if (win.transform.parent != parentRoot.transform)
            {
                win.transform.SetParent(parentRoot.transform, false);
            }
            win.Layer = parentWin.Layer;
            win.winCanvas = null;
            //GuiUtil.ApplyCull(win.Root, cullApplyMode, _Cull, true);
        }
        /// <summary>
        /// 移除一个窗口
        /// </summary>
        /// <param name="win">待移除的窗口</param>
        /// <param name="destToCull"></param>
        /// <param name="destRoot"></param>
        public void RemoveWindow(GWindow win, bool destToCull, Transform destRoot)
        {
            WinCanvas wc = win.winCanvas as WinCanvas;
            win.winCanvas = null;
            win.transform.SetParent(destRoot);
            if (destToCull != _Cull)
            {
                win.Root.SetCull(destToCull, true);
            }
            if (wc != null)
            {
                if (_WinCanvasList != null)
                {
                    if (_WinCanvasList.Remove(wc))
                    {
                        if (wc.order == _MaxOrder)
                        {
                            _MaxOrder -= OrderStep;
                        }
                    }
                    else
                    {
                        Debug.LogErrorFormat("canvas {0}不属于{1}层", wc.root.name, layerName);
                    }
                }
                WinCanvas.Gabage(wc, _Cull);
            }
        }
        /// <summary>
        /// 删除一个窗口
        /// </summary>
        /// <param name="win">待删除的窗口，如果窗口没有在当前canvas里，会抛出异常</param>
        public void DestroyWindow(GWindow win)
        {
            RemoveWindow(win, _Cull, null);
            U3DUtil.DestroyGameObject(win.Root);
        }

        /// <summary>
        /// 在本层级移动窗口
        /// </summary>
        /// <param name="win"></param>
        /// <param name="siblingIdx">层级索引，0表示最底部，-1表示最顶部</param>
        public void MoveWindowUpDown(GWindow win, int siblingIdx)
        {
            var wc = win.winCanvas as WinCanvas;
            if (wc != null)
            {//窗口有自己的canvas
                U3DUtil.SetSibling(wc.root, siblingIdx);
                if (_WinCanvasList != null)
                {
                    int idx = _WinCanvasList.IndexOf(wc);
                    if (idx == -1)
                        return;
                    //todo: 每个窗口都有自己的canvas，那么需要改变canvas的sort order
                    //以下解决方案有很多问题，比如某个窗口上播放的特效，已经设置了order，这样做特效可能被挡住
                    _WinCanvasList.RemoveAt(idx);
                    if (siblingIdx == 0)
                    {//移动到最底部
                        _WinCanvasList.Insert(0, wc);
                    }
                    else if (siblingIdx == -1)
                    {//移动到最顶部
                        _WinCanvasList.Add(wc);
                    }
                    _MaxOrder = BaseOrder - OrderStep;
                    for (int i = 0; i < _WinCanvasList.Count; ++i)
                    {
                        _MaxOrder += OrderStep;
                        _WinCanvasList[i].order = _MaxOrder;
                    }
                }
            }
            else
            {//窗口使用共享canvas（在层的根节点上，或者在子层节点上，或者在父窗口的某个节点上）
                U3DUtil.SetSibling(win.Root, siblingIdx);
            }
        }

        /// <summary>
        /// 点击测试
        /// </summary>
        /// <param name="pt"></param>
        /// <param name="result"></param>
        public void Raycast(PointerEventData pt, List<RaycastResult> result)
        {
            result.Clear();
            if (_Cull)
                return;
            if (_SharedCanvas != null)
            {
                if (_SharedCanvas.rayCaster.enabled)
                    _SharedCanvas.rayCaster.Raycast(pt, result);
            }
            else
            {
                for (int i = _WinCanvasList.Count - 1; i >= 0; --i)
                {
                    var wc = _WinCanvasList[i];
                    if (wc.rayCaster.enabled)
                    {
                        wc.rayCaster.Raycast(pt, result);
                        if (result.Count > 0)
                            break;
                    }
                }
            }
        }

        private bool IsLayerCastered(int lyer)
        {
            return lyer >= (int)WindowLayer.BottomMost && lyer <= (int)WindowLayer.Screen;
        }
    }
